package com.kob.backend.consumer.utils;

import com.alibaba.fastjson2.JSONObject;
import com.kob.backend.consumer.WebSocketServer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.locks.ReentrantLock;
//public class FriendGame extends Thread
public class FriendGame implements Runnable {
    final private Integer rows;
    final private Integer cols;
    final private Integer inner_walls_count;
    private int[][] g;
    final private static int[] dx = {-1 , 0, 1, 0}, dy = {0, 1, 0, -1};
    private final Player PlayerA, PlayerB;
    private  Integer nextStepA = null;
    private  Integer nextStepB = null;
    private ReentrantLock lock = new ReentrantLock();
    private String status = "playing";//playing -> finished
    private String loser = ""; //all： 平局 A: a输 B: b输
    private boolean isPVA = false;//是否人机操作
    private int [][] gNow = new int[13][14];//现场局面
    public FriendGame(Integer rows, Integer cols, Integer inner_walls_count, Integer userId, boolean isPVA ){
        this.rows = rows;
        this.cols = cols;
        this.inner_walls_count = inner_walls_count;
        this.g = new int[rows][cols];
        PlayerA = new Player(userId,-1,null ,rows - 2, 1, new ArrayList<>());
        PlayerB = new Player(userId,-1,null, 1, cols - 2, new ArrayList<>());
        this.isPVA = isPVA;
    }

    public Player getPlayerA() {
        return PlayerA;
    }

    public Player getPlayerB() {
        return PlayerB;
    }
    public void setNextStepA(Integer nextStepA) {
        lock.lock();
        try {
            this.nextStepA = nextStepA;
        }finally {
            lock.unlock();
        }
    }

    public void setNextStepB(Integer nextStepB) {
        lock.lock();
        try {
            this.nextStepB = nextStepB;
        }finally {
            lock.unlock();
        }

    }
    public int[][] getG(){
        return g;
    }
    private boolean check_connectivity(int sx, int sy, int tx, int ty){
        if(sx == tx && sy == ty) return true;
        g[sx][sy] = 1;
        for(int i = 0; i < 4; i ++){
            int x = sx + dx[i], y = sy + dy[i];
            if(x >= 0 && x < this.rows && y >= 0 && y < this.cols && g[x][y] == 0){
                if(check_connectivity(x,y,tx,ty)){
                    g[sx][sy] = 0;
                    return true;
                }
            }
        }
        g[sx][sy] = 0;
        return false;
    }
    private boolean draw(){
        for(int i = 0; i < this.rows; i ++){
            for(int j = 0; j < this.cols; j ++){
                g[i][j] = 0;
            }
        }

        for(int r = 0; r < this.rows; r ++){
            g[r][0] = g[r][this.cols - 1] = 1;
        }
        for(int c = 0; c < this.cols; c ++){
            g[0][c] = g[this.rows - 1][c] = 1;
        }
        Random random = new Random();
        for(int i = 0; i < this.inner_walls_count / 2; i ++){
            for(int j = 0; j < 1000; j ++){
                int r = random.nextInt(this.rows);
                int c = random.nextInt(this.cols);

                if(g[r][c] == 1|| g[this.rows - 1 - r][this.cols - 1-c] == 1) continue;;
                if(r == this.rows - 2 && c == 1 || r == 1 && c == this.cols - 2 ) continue;

                g[r][c] = g[this.rows - 1 - r ][this.cols - 1 - c] = 1;
                break;
            }
        }
        return check_connectivity(this.rows - 2, 1, 1, this.cols - 2);
    }

    public void creatMap(){
        for(int i = 0; i < 1000; i ++){
            if(draw()){
                break;
            }
        }
    }
    private String getMapString(){
        StringBuilder res = new StringBuilder();
        for(int i = 0; i < rows; i ++){
            for(int j = 0; j < cols; j ++){
                res.append(g[i][j]);
            }
        }
        return res.toString();
    }
    private String getInput(){
        //将当前的局面信息编码成字符串
        Player me, you;
        me = PlayerB;
        you = PlayerA;
        return getMapString() + "#" +
                me.getSx() + "#" +
                me.getSy()+"#("+
                me.getStepsString() + ")#" +
                you.getSx() + "#" +
                you.getSy() + "#(" +
                you.getStepsString() + ")";
    }
    private boolean check_tail_increasing(int step) {  // 检验当前回合，蛇的长度是否增加
        if (step <= 10) return true;
        return step % 3 == 1;
    }
    public List<Cell> getCells(int sx, int sy, String steps) {
        steps = steps.substring(1, steps.length() - 1);
        List<Cell> res = new ArrayList<>();
        int[] dx = {-1, 0, 1, 0}, dy = {0, 1, 0, -1};
        int x = sx, y = sy;
        int step = 0;
        res.add(new Cell(x, y));
        for (int i = 0; i < steps.length(); i ++ ) {
            int d = steps.charAt(i) - '0';
            x += dx[d];
            y += dy[d];
            res.add(new Cell(x, y));
            if (!check_tail_increasing( ++ step)) {
                res.remove(0);
            }
        }
        return res;
    }
    private int dfsFind(int x, int y, int cut){//当前位置最多可走多少步
        if(gNow[x][y] == 1 || cut == 20) return 0;//搜索20步之内最优解，未剪枝
        this.gNow[x][y] = 1;
        int[] dx = {-1, 0, 1, 0}, dy = {0, 1, 0, -1};
        int nx, ny;
        int res = 0;
        for (int i = 0; i < 4; i ++) {
            nx = x + dx[i];
            ny = y + dy[i];
            if (nx >= 0 && nx < 13 && ny >= 0 && ny < 14 && this.gNow[nx][ny] == 0) {
                int t = dfsFind(nx, ny, cut + 1);
                res = Math.max(res, t);
            }
        }
        this.gNow[x][y] = 0;
        return res + 1;
    }
    //AI决策
    private int getDirection(String situation){
        for(int i = 0; i < 13; i ++){
            for(int j = 0; j < 14; j ++){
                gNow[i][j] = 0;
            }
        }
        String[] strs = situation.split("#");
        for (int i = 0, k = 0; i < 13; i ++ ) {
            for (int j = 0; j < 14; j ++, k ++ ) {
                if (strs[0].charAt(k) == '1') {
                    this.gNow[i][j] = 1;
                }
            }
        }
        int aSx = Integer.parseInt(strs[1]), aSy = Integer.parseInt(strs[2]);
        int bSx = Integer.parseInt(strs[4]), bSy = Integer.parseInt(strs[5]);
        List<Cell> aCells = getCells(aSx, aSy, strs[3]);
        List<Cell> bCells = getCells(bSx, bSy, strs[6]);
        for (Cell c: aCells) this.gNow[c.x][c.y] = 1;
        for (Cell c: bCells) this.gNow[c.x][c.y] = 1;
        int x = aCells.get(aCells.size() - 1).x;
        int y = aCells.get(aCells.size() - 1).y;
        int[] dx = {-1, 0, 1, 0}, dy = {0, 1, 0, -1};
        int nx, ny;
        int res = 0;
        int direction = 0;
        for(int i = 0; i < 4; i ++){
            nx = x + dx[i];
            ny = y + dy[i];
            int num = dfsFind(nx,ny,0);
            if(res<=num){
                res = num;
                direction = i;
            }
        }

        return direction;
    }
    private boolean nextStep(){
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if(isPVA){
            this.nextStepB = getDirection(getInput());
        }
        for(int i = 0; i < 50; i ++){
            try {
                Thread.sleep(100);
                lock.lock();
                try {
                    if(nextStepA != null && nextStepB != null){
                        PlayerA.getSteps().add(nextStepA);
                        PlayerB.getSteps().add(nextStepB);
                        return true;
                    }
                }finally {
                    lock.unlock();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        return false;
    }
    public boolean check_valid(List<Cell> cellsA, List<Cell> cellsB){
        int n = cellsA.size();
        Cell cell = cellsA.get(n - 1);
        if(g[cell.x][cell.y] == 1 ) return false;

        for(int i = 0; i < n - 1; i ++){
            if(cellsA.get(i).x == cell.x && cellsA.get(i).y == cell.y)
                return false;
        }

        for(int i = 0; i < n - 1; i ++){
            if(cellsB.get(i).x == cell.x && cellsB.get(i).y == cell.y)
                return false;
        }

        return true;
    }
    private void judge(){   //判断两名玩家下一步操作是否合法
        List<Cell> cellsA = PlayerA.getCells();
        List<Cell> cellsB = PlayerB.getCells();

        boolean validA = check_valid(cellsA,cellsB);
        boolean validB = check_valid(cellsB,cellsA);

        if(!validA || !validB) {
            status = "finished";
            if(!validA && !validB){
                loser = "all";
            }else if(!validA){
                loser = "A";
            }else {
                loser = "B";
            }
        }

    }
    private void sendMove(){ //向两名玩家传递移动信息
        lock.lock();
        try {
            JSONObject resp = new JSONObject();
            resp.put("event","f_move");
            resp.put("a_direction",nextStepA);
            resp.put("b_direction",nextStepB);
            if(WebSocketServer.users.get(PlayerA.getId())!=null){
                WebSocketServer.users.get(PlayerA.getId()).sendMseeage(resp.toJSONString());
            }
            nextStepA = nextStepB = null;
        }finally {
            lock.unlock();
        }

    }
    private void sendResult(){ //向客户端返回结果
        JSONObject resp = new JSONObject();
        resp.put("event","result");
        resp.put("loser",loser);
        if(WebSocketServer.users.get(PlayerA.getId())!=null){
            WebSocketServer.users.get(PlayerA.getId()).sendMseeage(resp.toJSONString());
        }
    }

    @Override
    public void run() {
        for(int i = 0; i < 1000; i ++){
            if(nextStep()){
                judge();
                if("playing".equals(status)){
                    sendMove();
                }else {
                    sendResult();
                    break;
                }
            }else {
                status = "finished";
                lock.lock();
                try {
                    if(nextStepA == null && nextStepB == null){
                        loser = "all";
                    }else if(nextStepA == null){
                        loser = "A";
                    }else {
                        loser = "B";
                    }
                }finally {
                    lock.unlock();
                }
                sendResult();
                break;

            }
        }
    }
}
